﻿#include"XQZipPack.hpp"
#include"XAlgorithm.h"
#include"XQCoding.h"
#include"zlib/unzip.h"
#include"zlib/zip.h"
#include<QDir>
QByteArray XQZipPack::unzip(const QByteArray& data)
{
	QByteArray outData;//输出的数据数组
	outData.resize(data.size()+100);//重置大小
	z_stream strm;
	strm.zalloc = NULL;
	strm.zfree = NULL;
	strm.opaque = NULL;

	strm.avail_in = data.size();
	strm.avail_out = outData.size();
	strm.next_in = (Bytef*)data.data();
	strm.next_out = (Bytef*)&outData.front();

	int err = -1;
	err = inflateInit2(&strm, MAX_WBITS + 16);
	if (err == Z_OK)
	{
		err = inflate(&strm, Z_FINISH);
		if (err != Z_STREAM_END) 
		{
			inflateEnd(&strm);
			return QByteArray();
		}
	}
	else 
	{
		inflateEnd(&strm);
		return QByteArray();
	}
	inflateEnd(&strm);
	outData.resize(strm.total_out);
	return std::move(outData);
}
QByteArray XQZipPack::decompress(const QByteArray& data)
{
	const int bufferSize = 1024 * 32;
	char buffer[bufferSize];
	QByteArray result;

	z_stream stream;
	stream.zalloc = Z_NULL;
	stream.zfree = Z_NULL;
	stream.opaque = Z_NULL;
	stream.avail_in = data.size();
	stream.next_in = (Bytef*)data.constData();
	stream.avail_out = bufferSize;
	stream.next_out = (Bytef*)buffer;

	inflateInit2(&stream, -MAX_WBITS);
	while (stream.avail_in != 0) {
		int ret = inflate(&stream, Z_NO_FLUSH);
		if (ret == Z_STREAM_ERROR) {
			qFatal("Failed to decompress data: stream error.");
		}
		else if (ret == Z_NEED_DICT) {
			qFatal("Failed to decompress data: need dictionary.");
		}
		else if (ret == Z_MEM_ERROR) {
			qFatal("Failed to decompress data: out of memory.");
		}
		else if (ret == Z_DATA_ERROR) {
			qFatal("Failed to decompress data: input data is corrupted.");
		}

		int have = bufferSize - stream.avail_out;
		if (have > 0) {
			result.append(buffer, have);
			stream.avail_out = bufferSize;
			stream.next_out = (Bytef*)buffer;
		}
	}

	inflateEnd(&stream);
	return result;
}
QString XQZipPack::unPackageGetCurrentFileInfo(unzFile* unzfile ,const QString& UnPackageDir)
{
	//解析zip文件
	unz_file_info64  pFileInfo;
	char szZipFName[BUFFER_SIZE];
	//解析得到zip中的文件信息
	int nReturnValue = unzGetCurrentFileInfo64(*unzfile, &pFileInfo, szZipFName, 1024,
		NULL, 0, NULL, 0);
	if (nReturnValue != UNZ_OK)
	{
		emit progressInfo(QString("获取文件信息失败:") + szZipFName);
		return QString();
	}
	m_zipInfo.addIndexes();
	//qDebug() << szZipFName;
	//判断是文件夹还是文件
	if (QString(szZipFName).right(1) == "/")//读取到的是目录
	{
		QString strDiskPath = UnPackageDir + "/" + szZipFName;
		strDiskPath = strDiskPath.replace("/", "\\");
		emit progressInfo(QString("正在创建目录:") + strDiskPath);
	}
	else//读取到的是文件
	{
		//创建文件,写入的文件
		QString DiskFile = UnPackageDir + "/";
		DiskFile += QString::fromLocal8Bit(szZipFName, strlen(szZipFName));
		DiskFile = DiskFile.replace("\\", "/");

		//创建对应目录
		QDir filedir(DiskFile.left(DiskFile.lastIndexOf('/')));
		filedir.mkpath(".");
		return QString::fromLocal8Bit(szZipFName, strlen(szZipFName));
	}
	return QString();
}
void XQZipPack::unPackage(const QString& PackageFilePath, const QString& UnPackageDir, const QString& password)
{
	int nReturnValue = 0;
	//打开zip文件
	unzFile unzfile = unzOpen64(PackageFilePath.toLocal8Bit().data());
	if (unzfile == NULL)
	{
		emit progressInfo(QString("打开文件失败:") + PackageFilePath);
		return;
	}
	m_zipInfo.clear();
	m_zipInfo.type = ZipPackType::unzip;
	emit zipStart(m_zipInfo);
	//获取zip文件的信息
	unz_global_info64 pGlobalInfo ;
	nReturnValue = unzGetGlobalInfo64(unzfile, &pGlobalInfo);
	if (nReturnValue != UNZ_OK)
	{
		emit progressInfo(QString("获取打包文件信息失败:") + PackageFilePath);
		return;
	}
	/*m_zipInfo.clear();*/
	m_zipInfo.fileCount = pGlobalInfo.number_entry;//获取文件总数
	m_startTime = QDateTime::currentDateTime();
	char* ReadBuffer = new char[BUFFER_SIZE] {0};
	//存放从zip中解析出来的内部文件名
	for (int i = 0; i < pGlobalInfo.number_entry; i++)
	{
		//读取到的是文件
		QString path=unPackageGetCurrentFileInfo(&unzfile, UnPackageDir);
		if(!path.isEmpty())
		{
			//创建文件,写入的文件
			QString DiskFile = UnPackageDir + "/";
			DiskFile += path;
			DiskFile = DiskFile.replace("\\", "/");
			
			//创建对应目录
			QDir filedir(DiskFile.left(DiskFile.lastIndexOf('/')));
			filedir.mkpath(".");
			//打开对应文件
			QFile file(DiskFile);
			if (!file.open(QIODeviceBase::OpenModeFlag::WriteOnly))
			{
				unzCloseCurrentFile(unzfile);
				unzClose(unzfile);
				delete[] ReadBuffer;
				return;//文件打开失败
			}
			//打开压缩包中文件
			nReturnValue = unzOpenCurrentFilePassword(unzfile,password.isEmpty()?NULL:password.toLocal8Bit().data());
			if (nReturnValue != UNZ_OK)
			{
				emit progressInfo(QString("密码错误，解密文件失败:") + DiskFile);
				unzCloseCurrentFile(unzfile);
				unzClose(unzfile);
				delete[] ReadBuffer;
				return;
			}
			//读取文件
			m_zipInfo.setFile(QFileInfo(DiskFile));
			
			emit progressInfo("正在写入文件内容");
			int ReadFileSize = 0;//返回读取的大小
			do
			{
				ReadFileSize = unzReadCurrentFile(unzfile, ReadBuffer, BUFFER_SIZE);
				if (ReadFileSize > 0)
				{
					file.write(ReadBuffer, ReadFileSize);
					m_zipInfo.addSize(ReadFileSize);
					sendProgress();
				}
			} while (ReadFileSize > 0);
			if (ReadFileSize < 0)                //读取文件失败
			{
				unzCloseCurrentFile(unzfile);
				unzClose(unzfile);
				delete[] ReadBuffer;
				return;
			}
			emit progressInfo(DiskFile+" 文件解压完成");
			file.close();
		}
		unzGoToNextFile(unzfile);
	}
	//关闭
	if (unzfile)
	{
		unzClose(unzfile);
	}
	delete[] ReadBuffer;
	emit zipProgress(m_zipInfo);
	emit zipFinish(m_zipInfo);
}
void XQZipPack::unPackFileFromPackge(const QString& PackageFilePath, const QString& UnPackageDir, const QString& fileNameInPackage, const QString& password)
{
	//打开
	unzFile zFile = unzOpen64(PackageFilePath.toLocal8Bit().data());
	if (zFile == NULL)
	{
		emit progressInfo("文件打开失败:"+ PackageFilePath);
		return;
	}
	//定位文件
	int err = unzLocateFile(zFile, fileNameInPackage.toLocal8Bit().data(), 0);
	if (UNZ_OK != err)
	{
		qDebug() << "unzLocateFile failed!err:" << err;
		return;
	}
	//获取当前文件信息
	unsigned int BufSize = 512;
	char* szFileName_WithPath = new char[BufSize];
	unz_file_info zFileInfo;
	if (UNZ_OK != unzGetCurrentFileInfo(zFile, &zFileInfo, szFileName_WithPath, BufSize, NULL, 0, NULL, 0))
	{
		emit progressInfo("获取文件信息失败");
		return;
	}
	//打开当前文件
	if (UNZ_OK != unzOpenCurrentFile(zFile))
	{
		emit progressInfo("打开当前文件失败");
		return;
	}
	//写入文件
	QString FileName = UnPackageDir + "/" + fileNameInPackage;
	QFile file (FileName) ;
	if (!file.open(QIODeviceBase::OpenModeFlag::WriteOnly))
	{
		emit progressInfo("解压:"+fileNameInPackage+"失败");
	}
	char ReadBuffer[BUFFER_SIZE]{ 0 };
	int ReadFileSize = 0;
	do
	{
		ReadFileSize= unzReadCurrentFile(zFile, ReadBuffer, BUFFER_SIZE);
		if (ReadFileSize > 0)
		{
			file.write(ReadBuffer, ReadFileSize);
		}
	} while (ReadFileSize > 0);
	
	//解压文件,到fileData中

	unzClose(zFile);
	//释放资源
	delete[] szFileName_WithPath;
	szFileName_WithPath = NULL;
}